package org.dreamwork.jasmine2.restful.client.gui.ui;

import org.dreamwork.jasmine2.restful.APIDefinition;
import org.dreamwork.jasmine2.restful.client.gui.util.ClassGenerator;
import org.dreamwork.secure.SecureContext;
import org.dreamwork.util.FileInfo;

import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import java.util.Map;

public class GenerateClassDialog extends JDialog {
    private JPanel contentPane;
    private JButton btnOK;
    private JButton btnCancel;
    private JTextField txtPackage;
    private JTextField txtPrefix;
    private JTextField txtSuffix;
    private JTextField txtOutput;
    private JButton btnBrowse;
    private JProgressBar progress;
    private JLabel txtMessage;

    private Map<String, APIDefinition> map;
    private JFileChooser chooser;
    private SecureContext context;
    private int index;

    public GenerateClassDialog (Map<String, APIDefinition> map, SecureContext context) {
        this.map = map;
        this.context = context;
        setContentPane (contentPane);
        setModal (true);
        getRootPane ().setDefaultButton (btnOK);

        btnOK.addActionListener (new ActionListener () {
            public void actionPerformed (ActionEvent e) {
                onOK ();
            }
        });

        btnCancel.addActionListener (new ActionListener () {
            public void actionPerformed (ActionEvent e) {
                onCancel ();
            }
        });

        setDefaultCloseOperation (DO_NOTHING_ON_CLOSE);
        addWindowListener (new WindowAdapter () {
            public void windowClosing (WindowEvent e) {
                onCancel ();
            }
        });

        contentPane.registerKeyboardAction (new ActionListener () {
            public void actionPerformed (ActionEvent e) {
                onCancel ();
            }
        }, KeyStroke.getKeyStroke (KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

        guiSetup ();
    }

    private void guiSetup () {
        chooser = new JFileChooser ();
        chooser.setFileSelectionMode (JFileChooser.DIRECTORIES_ONLY);

        btnBrowse.addActionListener (new ActionListener () {
            @Override
            public void actionPerformed (ActionEvent e) {
                try {
                    choose ();
                } catch (IOException ex) {
                    ex.printStackTrace ();
                }
            }
        });
    }

    private void onOK () {
        new Thread () {
            @Override
            public void run () {
                try {
                    generate ();
                } catch (IOException ex) {
                    ex.printStackTrace ();
                }
            }
        }.start ();
    }

    private void onCancel () {
        dispose ();
    }

    private void choose () throws IOException {
        int option = chooser.showOpenDialog (this);
        if (option == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile ();
            if (file != null) {
                txtOutput.setText (file.getCanonicalPath ());
            }
        }
    }

    private void generate () throws IOException {
        try {
            btnCancel.setEnabled (true);
            btnOK.setEnabled (true);

            String path = txtPackage.getText ().replace ('.', '/');
            File dir = new File (txtOutput.getText (), path);
            if (!dir.exists () && !dir.mkdirs ()) {
                throw new IOException ("Can't create dir " + dir.getCanonicalPath ());
            }
            String[] names = {
                    "HttpMethod.txt",
                    "RestfulAPIClient.txt",
                    "HttpUtil.txt",
                    "RestfulClientException.txt",
                    "NameValuePair.txt"
            };

            progress.setMaximum (names.length + map.size ());
            progress.setValue (0);

//            File base = new File ("../resource/");
            File base = new File ("D:\\tmp\\restful-client\\resource");
            for (String name : names) {
                progress.setValue (++ index);
                File file = new File (base, name);
                String className = FileInfo.getFileNameWithoutExtension (file.getCanonicalPath ());
                File target = new File (dir, className + ".java");
                copy (file, target);
            }

            File target = new File (dir, "RestfulApiContext.java");
            generateContext (target);

            for (final APIDefinition api : map.values ()) {
                SwingUtilities.invokeLater (new Runnable () {
                    @Override
                    public void run () {
                        txtMessage.setText ("Generating " + api.getTitle () + "...");
                        progress.setValue (++index);
                    }
                });
                ClassGenerator generator = new ClassGenerator (
                        txtPackage.getText (), txtPrefix.getText (),
                        txtSuffix.getText (), api, new File (txtOutput.getText ())
                );
                generator.generate ();
            }

            SwingUtilities.invokeLater (new Runnable () {
                @Override
                public void run () {
                    txtMessage.setText ("All class generated successfully.");
                }
            });
        } finally {
            btnCancel.setEnabled (true);
            btnOK.setEnabled (true);
        }
    }

    private void copy (File src, File target) throws IOException {
        if (target.exists ()) return;
        FileReader fr = new FileReader (src);
        FileOutputStream fos = new FileOutputStream (target);
        try {
            BufferedReader reader = new BufferedReader (fr);
            PrintWriter out = new PrintWriter (fos, true);
            String line;

            out.println ("package " + txtPackage.getText () + ";");
            out.println ();
            while ((line = reader.readLine ()) != null) {
                out.println ();
                out.print (line);
            }
            out.flush ();
        } finally {
            fr.close ();
            fos.flush ();
            fos.close ();
        }
    }

    private void generateContext (File target) throws IOException {
        FileOutputStream fos = new FileOutputStream (target);
        String baseUrl = System.getProperty ("api-url-prefix");
        String appId = System.getProperty ("app-id");

        if (baseUrl.endsWith ("/")) baseUrl = baseUrl.substring (0, baseUrl.length () - 1);

        try {
            PrintWriter out = new PrintWriter (fos, true);
            out.println ("package " + txtPackage.getText () + ";");
            out.println ();
            out.println ("import org.dreamwork.secure.AlgorithmMapping;");
            out.println ("import org.dreamwork.secure.SecureContext;");
            out.println ("import java.security.PublicKey;");
            out.println ();
            out.println ("public class RestfulApiContext {");
            out.println ("    public static SecureContext secureContext = new SecureContext ();");
            out.println ("    private static PublicKey publicKey;");
            out.printf  ("    public static final String baseURL = \"%s\";%n", baseUrl);
            out.printf  ("    public static final String appId = \"%s\";%n", appId);
            out.println ();
            out.println ("    static {");
            out.printf  ("        secureContext.setBlockEncryption (AlgorithmMapping.find (\"%s\"));%n", context.getBlockEncryption ().uri);
            out.printf  ("        secureContext.setSignature (AlgorithmMapping.find (\"%s\"));%n", context.getSignature ().uri);
            out.printf  ("        secureContext.setKeyTransport (AlgorithmMapping.find (\"%s\"));%n", context.getKeyTransport ().uri);
            out.println ("    }");
            out.println ();
            out.println ("    public static void setPublicKey (PublicKey kek) {");
            out.println ("        publicKey = kek;");
            out.println ("    }");
            out.println ();
            out.println ("    public static PublicKey getPublicKey () {");
            out.println ("        return publicKey;");
            out.println ("    }");
            out.print ("}");
            out.flush ();
        } finally {
            fos.flush ();
            fos.close ();
        }
    }
}